import math
import os
import sys
import time
import traceback
import warnings
from typing import Callable, Iterable

import img.img_loader as imgs
import pyautogui
import pygame
import PygameCrew as pycr
import requests
from calca import mltsplit, rbystr, symbol
from PygameCrew import SpVR, VerRect, text_part

from asset.constants import *

pycr.change_button_react(imgs.play_click)
#warnings.filterwarnings("ignore","libpng warning: iCCP: known incorrect sRGB profile")
def ords(string:str)->"tuple[int]":return tuple(map(ord," ad"))
def getfrom(D:dict,*values)->"tuple[str,object]|None":
    for v in values:
        if D.get(v) is not None:return v,D.get(v)
    return None
def combine(s1:"tuple[int,int]",s2:"tuple[int,int]",ratio:int=100):return (s1[0]+s2[0]/ratio,s1[1]+s2[1]/ratio)
def sgo(start:pycr.Pos,end:pycr.Pos,realstart:pycr.Pos):return realstart[0]+end[0]-start[0],realstart[1]+end[1]-start[1]
def isfloat(string:str)->"bool":
    try:float(string);return True
    except:return False
def maketrue(string:str)->"str|bool":
    if len(string) == 0 or string.isspace():return True
    return string
def pressany(keys:"Iterable[int]"):
    for k in keys:
        if pycr.PyCr_Value.press[k]:return True
    return False
# Game_Blocks Subclass : GBS
# Non-Game_Blocks : NGB
class Game_Blocks:
    def __init__(self,start:pycr.Pos,end:pycr.Pos,color:pycr.rgb,feature:dict=None):
        self.rect=pycr.SpVR(start,end,density=1.5,gravity=False,edgestop=False)
        self.color=color
        self.feature=feature
    def __str__(self):
        raise TypeError("String function in game blocks not defined")
    def form_block(self,name:str):
        return name+"(%s,%s,%s,%s)"%(str(self.rect.start),str(self.rect.end),str(self.color),pycr.stringify(self.feature))
    def __pycrput__(self):
        self.rect.run()
        self.drawrect()
    def __editput__(self):self.drawrect()
    def drawrect(self):pygame.draw.rect(pycr.screen,self.color,self.rect.rectvalue)
class light_color(Game_Blocks):
    "GBS : Self has an alpha"
    def __pycrput__(self):
        self.rect.run()
        pycr.drawsemi(self.rect,self.color,80)
class speed_active(Game_Blocks):
    """GBS : To subclass a speed-active object(Has or has no hit box,)"""
class effect_gravity(Game_Blocks):
    """GBS : To subclass a block which has gravity"""
    def __init__(self,start:pycr.Pos,end:pycr.Pos,color:pycr.rgb,feature:dict=None):
        self.rect=pycr.SpVR(start,end,density=1.5,gravity=True,edgestop=False)
        self.color=color
        self.feature=feature
class player_interactive(effect_gravity):
    """GBS : To subclass a block which can be affected by player"""
    def __init__(self,start:pycr.Pos,end:pycr.Pos,color:pycr.rgb,feature:dict=None):
        self.rect=pycr.player_SpVR(start,end,density=1.5,gravity=True,edgestop=False)
        self.color=color
        self.feature=feature
class not_runnable(speed_active):
    """GBS : To subclass a not-runnable object(Has or has no hit box,)"""
    def __init__(self,start:pycr.Pos,end:pycr.Pos,color:pycr.rgb,speed:pycr.Pos_Like,feature:dict=None):
        Game_Blocks.__init__(self,start,end,color,feature)
        self.runspeed=speed
    def __str__(self):return __class__.__name__+"(%s,%s,%s,%s,%s)"%(str(self.rect.start),str(self.rect.end),str(self.color),pycr.stringify(self.runspeed),pycr.stringify(self.feature))
class void_block(Game_Blocks):
    """NGB : To subclass a void(Has no hitbox)"""
class platform(speed_active):
    """GBS : To subclass a platform(Has hitbox)"""
    def __str__(self):return self.form_block(__class__.__name__)
class color_block():
    """NGB : To subclass a color"""
class colorless_block():
    """NGB : To subclass a no color"""
class killer(color_block,Game_Blocks):
    def __str__(self):return self.form_block(__class__.__name__)
class warp(color_block,Game_Blocks):
    def __str__(self):return self.form_block(__class__.__name__)
class hook(colorless_block,Game_Blocks):
    def __str__(self):return self.form_block(__class__.__name__)
    def __pycrput__(self):
        self.rect.run()
        pycr.screen.blit(imgs.img_hook,self.rect.start)
class conveyor(not_runnable,platform):
    def __str__(self):return __class__.__name__+"(%s,%s,%s,%s,%s)"%(str(self.rect.start),str(self.rect.end),str(self.color),pycr.stringify(self.runspeed),pycr.stringify(self.feature))
class trigger(light_color,color_block):
    def __init__(self,start:pycr.Pos,end:pycr.Pos,color:pycr.rgb,trig:str,feature:dict=None):
        self.trig=trig
        Game_Blocks.__init__(self,start,end,color,feature)
    def __str__(self):return __class__.__name__+"(%s,%s,%s,%s,%s)"%(str(self.rect.start),str(self.rect.end),str(self.color),pycr.stringify(self.trig),pycr.stringify(self.feature))
class timer(Game_Blocks,colorless_block):
    def __init__(self,start:pycr.Pos,color:pycr.rgb,etime:float,trig:str,starttrig:str,feature:dict=None):
        self.start=start
        self.color=color
        self.etime=etime
        self.trig=trig
        self.starttrig=starttrig
        self.feature=feature
        size=pycr._font[20].size("Timer "+str(self.etime))
        self.rect=pycr.VerRect(start,pycr.overlap(start,size))
        self.timer=None
    def start_timer(self):
        self.timer=time.time()
    def __str__(self):
        return __class__.__name__ + "(%s,%s,%s,%s,%s,%s)"%(str(self.start),str(self.color),str(self.etime),pycr.stringify(self.trig),pycr.stringify(self.starttrig),pycr.stringify(self.feature))
    def __pycrput__(self):
        if self.timer is not None:ts=rbystr(time.time()-self.timer,2)
        if self.timer is None:string="Timer "+str(self.etime)
        elif ts > self.etime:string="Timer End"
        else:string=str(ts)+" / "+str(self.etime)
        pycr.put(text_part(self.start,string,color=self.color))
class wind(light_color,colorless_block,not_runnable):
    def __str__(self):return __class__.__name__+"(%s,%s,%s,%s,%s)"%(str(self.rect.start),str(self.rect.end),str(self.color),pycr.stringify(self.runspeed),pycr.stringify(self.feature))
class water(light_color,void_block):
    def __str__(self):return self.form_block(__class__.__name__)
class player():
    def __init__(self,start:pycr.Pos,end:pycr.Pos,reacts:"tuple[int]",color:pycr.rgb=PLAYERCOLOR):
        """reacts : (jump, left, right)"""
        self.rect=pycr.player_SpVR(start,end)
        self.unmoved=True
        self.color=color
        self.reacts=reacts
        self.addspeed=0.2
        self._jump:bool=True
        self.jumpbonus=0
    def jump(self):
        self.unmoved=False
        if self._jump:
            self.jumpbonus=4+abs(self.rect.speed[0])/5
            self._jump=False
        elif self.jumpbonus > 1.1:
            self.rect.cspeed(0,-self.jumpbonus)
            self.jumpbonus=self.jumpbonus**0.65
        else:
            self.jumpbonus=0
    def clear_jump_data(self):
        self._jump=True
        self.jumpbonus=0
    def __pycrput__(self):
        pygame.draw.rect(pycr.screen,self.color,self.rect.rectvalue)
    def run(self):
        if pressany(self.reacts[0]):self.jump()
        else:
            self.clear_jump_data()
        this=False
        if pressany(self.reacts[1]):
            if symbol(self.rect.speed[0])==-1:self.rect.cgbacc=(-self.addspeed,0)
            else:self.rect.cgbacc=(-self.addspeed*3,0)
        elif pressany(self.reacts[2]):
            if symbol(self.rect.speed[0])==1:self.rect.cgbacc=(self.addspeed,0)
            else:self.rect.cgbacc=(self.addspeed*3,0)
        else:
            self.rect.cgbacc=(0,0)
            this=True
        self.unmoved=self.unmoved and this
        if not self.rect.has_no_verspeed:
            self._jump=False
        self.rect.run()
class push_block(player_interactive,platform):
    def __str__(self):return self.form_block(self.__class__.__name__)
class dire:
    def __init__(self,start:pycr.Pos, end:pycr.Pos, speed:float):
        xshift=end[0]-start[0]
        yshift=end[1]-start[1]
        self.speed=speed
        self.start=start
        self.end=end
        if xshift == 0:
            self.x=0
            self.y=self.speed*symbol(yshift)
        else:
            b=((yshift/xshift) if xshift != 0 else 0)
            self.x=math.sqrt((speed**2)/(b**2+1))*symbol(xshift)
            self.y=b*self.x
        self.s=(self.x,self.y)
        self.sym=(symbol(self.x),symbol(self.y))
    @property
    def ox(self):return self.s[0]
    @property
    def oy(self):return self.s[1]
    @property
    def osx(self):return self.sym[0]
    @property
    def osy(self):return self.sym[1]
    @property
    def ex(self):return self.end[0]
    @property
    def ey(self):return self.end[1]
    @property
    def sx(self):return self.start[0]
    @property
    def sy(self):return self.start[1]
    def turn(self, r:VerRect, outend:bool, outstart:bool):
        if outend:self.x, self.y=-abs(self.x)*self.osx,-abs(self.y)*self.osy
        elif outstart:self.x, self.y=abs(self.x)*self.osx,abs(self.y)*self.osy
    def outend(self,r:VerRect):return r.sx*self.osx>=self.ex*self.osx and r.sy*self.osy>=self.ey*self.osy
    def outstart(self,r:VerRect):return r.sx*self.osx<=self.sx*self.osx and r.sy*self.osy<=self.sy*self.osy
    def absolute(self,p:pycr.Pos):
        self.end=sgo(self.start,self.end,p)
        self.start=p
    def __str__(self):return "dire(%s, %s, %s)"%(str(self.start),str(self.end),str(self.speed))
class effectable_list(list):
    plyr:player=None
    def addif(self,obj:Game_Blocks):
        if isinstance(obj.rect,pycr.player_SpVR):
            self.append(obj)
            if isinstance(obj,player):self.plyr=obj
    def stopout(self,vr:"VerRect"):
        for obj in self:obj.stopout(vr)
    def run(self):
        for obj in self:obj.run()
    def __pycrput__(self):
        for obj in self:obj.__pycrput__()

def givenone()->None:return None
def objects(n:int,o:Callable=object):
    for i in range(n):yield o()
def double(s:object):return (s,s)
def getrev(obj:Game_Blocks,width:int=2)->"tuple[pygame.Surface,pycr.Pos]":return (pycr.chamber(obj.rect,reverse(obj.color),width),pycr.overlap2(obj.rect.start,(-width,-width)))
def drawrev(obj:Game_Blocks,width:int=2)->None:pycr.screen.blit(*getrev(obj,width))
def exitnone(s:object):return (isinstance(s,str) and s.lower() == "exit") or s is None or s is pycr.QUIT
def editput(s:pycr.SupportsPyCrOperations):s.__pycrput__()
def makefeature(gb:Game_Blocks):
    if not isinstance(gb.feature,dict):gb.feature=dict()
def makeeditmode(gb:Game_Blocks,edit:str,to:object):
    makefeature(gb)
    if gb.feature.get(edit) is None:gb.feature[edit]=to
def cleareditmode(gb:Game_Blocks,edit:str,to:object):
    makefeature(gb)
    gb.feature[edit]=to
def mkdoc(o:Game_Blocks)->str:
    result="Type: "+o.__class__.__name__
    result+="\nFeature: "+pycr.stringify(o.feature,',\n')
    if isinstance(o,(trigger,timer)):
        result+="\nTrigger str: "+o.trig
    if isinstance(o,timer):
        result+="\nTime: "+o.etime
        result+="\nRespond: "+o.starttrig
    if isinstance(o,not_runnable):
        result+="\nX Speed: "+pycr.stringify(o.runspeed[0])
        result+="\nY Speed: "+pycr.stringify(o.runspeed[1])
    return result
def setofstring(a:str,b:str):S=set(a);S.update(b);return "".join(S)
Movable=(player,player_interactive)
Pusher=(player,platform)
Appear=pycr.named_object("Appear")
